<?php 
/**
 * Класс, который реализирует базовые операции выборки
 *
 */
class FRM_Db_Table_Abstract extends Zend_Db_Table
{
	/**
	 * Масив списка полей, которые должны отображатся в DataGrid
	 *
	 * @var array
	 */
	public $gridViewColums = array();
	/**
	 * Ссылка на класс FRM_Db_Select_Abstract с заполнеными from() и join()
	 *
	 * @var FRM_Db_Select_Abstract
	 */
	protected $currentSelect = null;

	/**
	 * Ссылка на класс FRM_Db_Select_Abstract с заполнеными from() и join()
	 *
	 * @var FRM_Db_Select_Abstract
	 */
	protected $currentWhere = null;

	/**
	 * Enter description here...
	 *
	 * @var unknown_type
	 */
	protected $orderField = '_order';

	/**
	 * Enter description here...
	 *
	 * @var unknown_type
	 */
	protected $showField = '_show';


	protected $treeFieldName;

	protected $treeControllerName;

	protected $treeForeignName;

	protected $comboKeyField;

	protected $detailClasses =array();

	/**
	 * Enter description here...
	 *
	 * @var unknown_type
	 */
	protected $keyField ='id';


	/**
	 * Enter description here...
	 *
	 * @var Zend_Auth
	 */
	public static $currentAuth;


	/**
	 * @var FRM_Db_Table_Abstract
	 */
	protected static $_instances;

	/**
	* Singleton pattern implementation
	* 
	* @return FRM_Db_Table_Abstract
	*/
	public static function getInstance($class=null)
	{
		if(is_null($class)){
			$bt = debug_backtrace();
			$class = $bt[1]['class'];
		}
		if (!isset(self::$_instances[$class])) {
			self::$_instances[$class] = new $class();
		}
		return self::$_instances[$class];
	}

	function getTreeFieldName(){
		return $this->treeFieldName;
	}

	function getTreeControllerName(){
		return $this->treeControllerName;
	}

	function getTreeForeignName(){
		return $this->treeControllerName;
	}

	public function getTableName(){
		return $this->_name;
	}

	public function setCurrentSelect($currentSelect = null){
		$this->currentSelect = $currentSelect;
		return $this;
	}

	public function setCurrentWhere($currentWhere = null){
		$this->currentWhere = $currentWhere;
		return $this;
	}

	/**
	 * Получем первую запись по указаном параметре
	 *
	 * @param string $paramName название параметра
	 * @param string $paramValue значение параметра
	 * @return array
	 */
	public function getRowByParam($where=null, $order=null)
	{
		return ($this->currentSelect)?$this->_getRowByParamSelect($where, $order):$this->_getRowByParamStandart($where, $order);
	}

	/**
	 * Получем первую запись по указаном параметре, исполюзуя стандартные методы Zend_Db_Table
	 *
	 * @param string $paramName название параметра
	 * @param string $paramValue значение параметра
	 * @return array
	 */
	private function _getRowByParamStandart($where=null, $order=null)
	{
		$res = $this->fetchRow($where, $order);
		return ($res)?$res->toArray():array();
	}


	/**
	 * Получем первую запись по указаном параметре, исполюзуя стандартные методы Zend_Db_Select
	 *
	 * @param string $paramName название параметра
	 * @param string $paramValue значение параметра
	 * @return array
	 */
	private function _getRowByParamSelect($where=null, $order=null)
	{
		//получаем  клон актуального Zend_Db_Select
		$select = clone $this->currentSelect;

		//Приводим условия поиска в формату, которій понимает Zend_Db_Select
		$select = $this->fixWhere($select, $where);

		//Приводим условия сортировки в формату, который понимает Zend_Db_Select
		$select = $this->fixOrder($select, $order);

		return $this->getAdapter()->fetchRow($select);
	}

	/**
	 * Позучаем поле в первой записи по указаном параметре
	 *
	 * @param string $fieldName название поля
	 * @param string $paramName название параметра
	 * @param string $paramValue значение параметра
	 * @return mixed
	 */
	public function getFieldByParam($fieldName, $paramName, $paramValue)
	{
		$where = "`$paramName` = '$paramValue'";
		$row  = $this->getRowByParam($where);

		return empty($row[$fieldName])?null:$row[$fieldName];
	}

	/**
	 * Проверяем, есть ли в зазе запись с єтим параметром и значением
	 *
	 * @param string $paramName название параметра
	 * @param string $paramValue значение параметра
	 * @return bool
	 */
	public function usedParam($paramName, $paramValue)
	{
		$row  = $this->getRowByParam($paramName, $paramValue);

		return !empty($row[$paramName]);
	}

	/**
	 * Получаем запись по id. Используя возможности Zend_Db_Table. 
	 * Если таблица использует связи, то лучше использовать getRowByParam (там будут и внешние поля)
	 *
	 * @param int $id зачение id
	 * @return array
	 */
	public function getRowById($id)
	{
		$res = $this->find($id)->current();
		return ($res)?$res->toArray():array();
	}

	/**
	 * Получаем указаний параметер по id. Используя возможности Zend_Db_Table. 
	 * Если таблица использует связи, то лучше использовать getFieldByParam (там будут и внешние поля)
	 *
	 * @param string $fieldName название поля
	 * @param int $id зачение id
	 * @return mixed
	 */
	public function getFieldById($fieldName, $id)
	{
		$row  = $this->getRowById($id);

		return empty($row[$fieldName])?null:$row[$fieldName];
	}

	/**
	 * Получение списока записей, по указаных парамертах
	 *
	 * @param mixed $where условия поиска
	 * @param mixed $order  условие сортировки
	 * @param int $count количество записей
	 * @param int $offset начианая с
	 * @return array
	 */
	public function getListWithPage($page=1, $limit=20, $where=null, $order=null)
	{
		$res = array();
		$data = array();

		$res['rowCount'] = $this->getRowCounts($where);

		if($res['rowCount']){
			$res['pages']=ceil($res['rowCount']/$limit);

			$page = intval($page);
			if(!$page || $page<0){
				$page = 1;
			}

			if($page>$res['pages']){
				$page=$res['pages'];
			}

			$res['page'] = $page;

			$res['rowFrom'] = ($page-1)*$limit+1;
			$res['rowTo'] = $page*$limit;

			if($res['rowTo']>$res['rowCount']){
				$res['rowTo']=$res['rowCount'];
			}

			$start = ($page-1)*$limit;
			$data = $this->getList($where, $order, $limit,$start);
		}

		$res['data'] = $data;

		return $res;
	}

	public function getAllWithPage($page=1, $limit=20, $where=null, $order=null, $action=null){
		$res = $this->getListWithPage($page, $limit, $where, $order, $action);
		if(!empty($res['data'])){
			$res['data'] = $this->setDetailData($res['data'], $action);
		}
		return $res;
	}

	public function getList($where=null, $order=null, $count=null, $offset=null)
	{

		if(!is_null($this->currentWhere)){
			if(is_null($where)){
				$where = $this->currentWhere;
			}else{
				if(is_array($where)){
					$where[]=$this->currentWhere;
				}else{
					$where = array($this->currentWhere, $where);
				}
			}
		}
		return ($this->currentSelect)?$this->_getListSelect($where, $order, $count, $offset):$this->_getListStandart($where, $order, $count, $offset);
	}


	/**
	 * Получение списока записей, по указаных парамертах, исполюзуя стандартные методы Zend_Db_Table
	 *
	 * @param mixed $where условия поиска
	 * @param mixed $order  условие сортировки
	 * @param int $count количество записей
	 * @param int $offset начианая с
	 * @return array
	 */
	private function _getListStandart($where=null, $order=null, $count=null, $offset=null)
	{
		$res = $this->fetchAll($where, $order,  $count, $offset);
		return ($res)?$res->toArray():array();
	}

	/**
	 *  Приводим условия поиска в формату, который понимает Zend_Db_Select
	 *
	 * @param Zend_Db_Select $select
	 * @param mixed $where
	 * @return Zend_Db_Select
	 */
	protected  function fixWhere($select, $where=null){
		if($where){
			if (!is_array($where)) {
				$where = array($where);
			}
			foreach ($where as $key => $val) {
				if (is_int($key)) {
					$select->where($val);
				} else {
					$select->where($key, $val);
				}
			}
		}
		return $select;
	}

	/**
	 *  Приводим условия сортирование в формату, который понимает Zend_Db_Select
	 *
	 * @param Zend_Db_Select $select
	 * @param mixed $order
	 * @return Zend_Db_Select
	 */
	protected  function fixOrder($select, $order=null){
		if($order){
			if (!is_array($order)) {
				$order = array($order);
			}

			foreach ($order as $val) {
				$select->order($val);
			}
		}
		return $select;
	}

	/**
	 * Получение списока записей, по указаных парамертах, исполюзуя стандартные методы Zend_Db_Select
	 *
	 * @param mixed $where условия поиска
	 * @param mixed $order  условие сортировки
	 * @param int $count количество записей
	 * @param int $offset начианая с
	 * @return array
	 */
	private function _getListSelect($where=null, $order=null, $count=null, $offset=null)
	{
		//получаем  клон актуального Zend_Db_Select
		$select = clone $this->currentSelect;

		//Приводим условия поиска в формату, которій понимает Zend_Db_Select
		$select = $this->fixWhere($select, $where);

		//Приводим условия сортировки в формату, который понимает Zend_Db_Select
		$select = $this->fixOrder($select, $order);

		//Назначаем лимит
		if($count){
			$select->limit( $count, $offset);
		}
		
//		print $select->__toString()."<br>";

		//$l = new Admin_MyLog();
		//$l->insert(array('text'=>$select->__toString()));
		return $this->getAdapter()->fetchAll($select);

	}

	/**
	 * Получение количевство записей в базе по указаном условии поиска
	 *
	 * @param mixed $where
	 * @return int
	 */
	public function getRowCounts($where=null){
		if(!is_null($this->currentWhere)){
			if(is_null($where)){
				$where = $this->currentWhere;
			}else{
				if(is_array($where)){
					$where[]=$this->currentWhere;
				}else{
					$where = array($this->currentWhere, $where);
				}
			}
		}
		return ($this->currentSelect)?$this->_getRowCountsSelect($where):$this->_getRowCountsStandart($where);
	}

	/**
	 * Получение количевство записей в базе по указаном условии поиска
	 * Используем стандартный Zend_Db_Table
	 *
	 * @param mixed $where
	 * @return int
	 */
	private function _getRowCountsStandart($where=null){
		$select = $this->getAdapter()->select();

		$select->from($this->_name, 'count(*)');
		$select = $this->fixWhere($select, $where);

		return (int)$this->getAdapter()->fetchOne($select);
	}

	/**
	 * Получение количевство записей в базе по указаном условии поиска
	 * Используем методы Zend_Db_Select
	 * 
	 * @param mixed $where
	 * @return int
	 */
	private function _getRowCountsSelect($where=null){
		//получаем  клон актуального Zend_Db_Select
		$select = clone $this->currentSelect;

		$select = $this->fixWhere($select, $where);

		//Обнуляем секцию FROM
		$select->reset( FRM_Db_Select_Abstract::COLUMNS);

		//Получаем количевство записей
		return (int)$this->getAdapter()->fetchOne(str_replace("SELECT FROM", "SELECT count(*) FROM", $select));
	}

	/**
	 * Получение исоциативного списка (для комбо например) по заданым параметрам
	 *
	 * @param string $display поле-результат для асоциативного масива
	 * @param string $key поле-ключ для асоциативного масива
	 * @param string $empty тект для нулевого ключа. (для комбо например "виберите...")
	 * @param mixed $where условия поиска
	 * @param mixed $order условие сортировки
	 * @param int $count количество записей
	 * @param int $offset начианая с...
	 * @return array
	 */
	public function getListAssoc($display='name', $key='id', $empty='', $where=null, $order=null, $count=null, $offset=null)
	{
		return FRM_Tools_Array::arrayToAssoc($this->getList($where, $order,  $count, $offset), $display,$key, $empty);
	}

	public function getListExtjsFormat($display='name', $key='id', $where=null, $order=null, $count=null, $offset=null)
	{
		return FRM_Tools_Array::arrayExtjsFormat($this->getList($where, $order,  $count, $offset), $display,$key);
	}

	public  function getCombo($id, $display='l_name', $key='id'){
		$where = null;
		if(!is_null($id) && !is_null($this->comboKeyField)){
			$where = $this->e("`{$this->comboKeyField}`=?", $id);
		}
		return $this->getListExtjsFormat($display, $key, $where);
	}

	/**
	 * Изменение поля по указаным параметрам
	 *
	 * @param string $fieldName название поля
	 * @param string $fieldValue новое значение поля
	 * @param string $keyFieldValue значение ключевого поля
	 * @param string $keyFieldName название ключевого поля
	 */
	public function updatField($fieldName, $fieldValue, $keyFieldValue, $keyFieldName = 'id')
	{
		$this->update(array($fieldName=>$fieldValue), $this->getAdapter()->quoteInto("`$keyFieldName` = ?", $keyFieldValue));
	}

	public function getTreeData($keyValue = null, $dispayField = 'l_name'){
		$filed = 'id_parent';
		$res = array();
		if(in_array($filed, $this->_cols)){
			if(is_null($keyValue)){
				$keyValue = 0;
			}
			$list = $this->getList("$filed = '$keyValue'");
			$leaf=true;
			//$useTree = true;
		}else {
			$list = $this->getList();
			$leaf=true;
		}
		if(!is_null($list)){
			foreach ($list as $v){
				if(!isset($v[$dispayField]) && isset($v['name'])){
					$v[$dispayField] = $v['name'];
				}
				$res[] = array('text'=>$v[$dispayField], 'leaf'=>$leaf, $this->treeForeignName=>$v['id']);
			}
		}
		//array('text'=>'fgsfsr', 'leaf'=>true, 'id_city'=>'51', 'cls'=>'folder');
		return $res;
	}

	public function e($str, $parram){
		return $this->getAdapter()->quoteInto($str, $parram);
	}

	public function setDetailData($data, $action=null){
		if(is_array($this->detailClasses))
		{
			foreach ($data as $k=>$row){
				foreach ($this->detailClasses as $v){					
					if(!isset($v['action']) || $action && isset($v['action']) && in_array($action, $v['action'])){						
						if(!empty($v['params'])){
							$o = new $v['class']($v['params']);
						}else {
							$o = new $v['class'];
						}
						if(empty($v['useLangs'])){

							$data[$k][$v['name']] = $o->{$v['method']}($row[$v['key']]);
						}else {

							foreach ($this->langs as $lang=>$langname){
								$res = $o->{$v['method']}($row[$v['key']], $lang);								
								foreach ($v['cols'] as $col){
									$data[$k][$col][$lang] = (!empty($res[$col]))?$res[$col]:'';
								}
							}
						}
					}
				}
			}
		}
		return $data;
	}

}